use super::{
    proto::{make_ota_complete, make_ota_translate, make_ping, make_request, make_session},
    resp::{wait_ack, wait_pong, Message, MutRx, TaskInfo},
    serial::ModeSConn,
};
use crate::{
    conn::{
        proto::{cmd, make_ota_start, make_query},
        resp::wait_result,
    },
    error::{error_of, AppErr, AppErrMap},
    utils::new_bytes,
};
use std::time::Duration;
use tokio::time::timeout;

const OTA_MAX_SIZE: usize = 224;
const OTA_PROGRESS: &'static str = "OTA_PROGRESS";

impl ModeSConn {
    pub async fn ping(&self, dst: u8) -> Result<(), AppErr> {
        let buf = make_ping(dst);
        let mut rx = self.recv_mq.get_rx().await;
        for _ in 0..3 {
            self.write_raw(&buf).await?;
            let result = wait_pong(&mut rx).await;
            if result.is_ok() {
                return Ok(());
            }
        }
        error_of("ping 3次失败")
    }

    pub async fn session<'a>(&self, rx: &mut MutRx<'a>, dst: u8, seq: u8) -> Result<(), AppErr> {
        let buf = make_session(dst, seq);
        for i in 0..3 {
            self.write_raw(&buf).await?;
            let result = wait_ack(rx, seq).await;
            if result.is_ok() {
                return Ok(());
            }
            println!("session ack 超时:{}", i + 1);
        }
        error_of("session 3次失败")
    }

    pub async fn query(&self, dst: u8, cmd: u8) -> Result<Message, AppErr> {
        let buf = make_query(dst, cmd);
        let mut rx = self.result_mq.get_rx().await;
        for _ in 0..3 {
            self.write_raw(&buf).await?;
            let msg = wait_result(&mut rx, cmd).await;
            if let Ok(msg) = msg {
                return Ok(msg);
            }
        }
        error_of("query 3次失败")
    }

    async fn query_task(&self, dst: u8) -> Result<TaskInfo, AppErr> {
        let msg = self.query(dst, cmd::QUERY_TASK).await?;
        let info = msg.parse_task_info()?;
        Ok(info)
    }

    async fn wait_response<'a>(
        &self,
        rx: &mut MutRx<'a>,
        dst: u8,
        seq: u8,
        cmd: u8,
    ) -> Result<Message, AppErr> {
        loop {
            let result = timeout(Duration::from_secs(3), rx.recv()).await;
            match result {
                Ok(Some(v)) => {
                    return Ok(v);
                }

                Ok(None) => {
                    return error_of("接收异常None");
                }

                Err(_) => {}
            };

            let info = self.query_task(dst).await?;
            if (info.seq != seq) || (info.cmd != cmd) {

                let result = timeout(Duration::from_secs(1), rx.recv()).await;
                match result {
                    Ok(Some(v)) => {
                        return Ok(v);
                    }
    
                    Ok(None) => {
                        return error_of("接收异常None");
                    }
    
                    Err(_) => {}
                };

                return error_of("任务执行异常");
            }
        }
    }

    pub async fn request_with_buf(&self, buf: &[u8]) -> Result<Message, AppErr> {
        let dst = buf[4];
        let seq = buf[6];
        let cmd = buf[7];
        let mut rx = self.recv_mq.get_rx().await;
        self.session(&mut rx, dst, seq.wrapping_sub(1)).await?;
        for i in 0..3 {
            self.write_raw(&buf).await?;
            let result = wait_ack(&mut rx, seq).await;
            if result.is_ok() {
                return self.wait_response(&mut rx, dst, seq, cmd).await;
            }
            println!("等待ack超时:{}", i + 1);
        }
        error_of("request ACK超时")
    }

    async fn get_seq(&self) -> u8 {
        let mut seq_lock = self.seq_lock.lock().await;
        let seq = *seq_lock;
        *seq_lock = seq.wrapping_add(1);
        seq
    }

    pub async fn request(&self, dst: u8, cmd: u8, data: &[u8]) -> Result<Message, AppErr> {
        let seq = self.get_seq().await;
        let buf = make_request(dst, seq, cmd, data);
        return self.request_with_buf(&buf).await;
    }

    async fn ota_start(
        &self,
        buf: &mut [u8],
        dst: u8,
        seq: u8,
        pkg_size: u32,
    ) -> Result<(), AppErr> {
        let len = make_ota_start(buf, dst, seq, pkg_size);
        let msg = self.request_with_buf(&buf[..len]).await?;
        msg.res_assert()
    }

    async fn ota_translate(
        &self,
        buf: &mut [u8],
        dst: u8,
        seq: &mut u8,
        data: &[u8],
    ) -> Result<(), AppErr> {
        let len = data.len();
        let mut index = 0;
        let mut id = 0;

        while index < len {
            let mut n = len - index;
            if n > OTA_MAX_SIZE {
                n = OTA_MAX_SIZE;
            }

            let buf_len = make_ota_translate(buf, dst, *seq, id, &data[index..(index + n)]);

            self.request_with_buf(&buf[..buf_len]).await?.res_assert()?;

            index += n;
            id += 1;
            *seq = seq.wrapping_add(1);

            self.window
                .emit(OTA_PROGRESS, index * 100 / len)
                .print_if_err();
        }

        Ok(())
    }

    async fn ota_complete(
        &self,
        buf: &mut [u8],
        dst: u8,
        seq: u8,
        data: &[u8],
    ) -> Result<(), AppErr> {
        let len = make_ota_complete(buf, dst, seq, data);
        self.request_with_buf(&buf[..len]).await?.res_assert()?;
        Ok(())
    }

    pub async fn ota(&self, dst: u8, data: &[u8]) -> Result<(), AppErr> {
        let pkg_size = data.len();
        let mut seq = self.get_seq().await;
        let mut buf = new_bytes(OTA_MAX_SIZE * 2);
        self.ota_start(&mut buf, dst, seq, pkg_size as u32).await?;
        seq = seq.wrapping_add(1);
        self.ota_translate(&mut buf, dst, &mut seq, data).await?;
        self.ota_complete(&mut buf, dst, seq, data).await?;
        Ok(())
    }
}
